import Joi from 'joi';
import set from 'lodash/set';
import { format as csvFormat } from '@fast-csv/format';
import { pathOr } from 'ramda';

import debugCheck from '@kuiper-helpers/debug-check';
import joiToFilterArg from '../helpers/joiToFilterArg';
import errorCodesBuilder from '../helpers/errorCodesBuilder';
import extractQueryFieldByJoi from '../helpers/extractQueryFieldByJoi';
import { datetime } from '../../common/formatter';
import {
	LOG_SOURCE_DICT,
	LOG_MODULE_DICT,
	LOG_ACTION_DICT,
} from '../../common/constants';
const errorCodes = errorCodesBuilder();

export const opsLog = Joi.object({
	_id: Joi.string(),
	uid: Joi.string(),
	level: Joi.string().description('消息等级'),
	username: Joi.string().allow(null).description('用户名'),
	module: Joi.string().allow(null).description('模块'),
	logSource: Joi.string().description('日志源'),
	ip: Joi.string().description('IP'),
	affects: Joi.array().items(Joi.string()).description('影响对象'),
	message: Joi.string().description('消息'),
	detail: Joi.any().description('附加信息'),
	createTime: Joi.date().description('时间'),
	actionType: Joi.string().allow(null).description('操作类型'),
	actionMessage: Joi.string().allow(null).description('操作类型消息'),
}).label('opsLog');

export const loginLog = Joi.object({
	_id: Joi.string(),
	module: Joi.string().allow(null).description('模块'),
	level: Joi.string().description('消息等级'),
	username: Joi.string().description('用户名'),
	uid: Joi.string().allow(null).description('用户ID'),
	ip: Joi.string().description('IP'),
	message: Joi.string().description('消息'),
	detail: Joi.any().description('附加信息'),
	actionType: Joi.string().allow(null).description('操作类型'),
	createTime: Joi.date().description('时间'),
	actionMessage: Joi.string().allow(null).description('操作类型消息'),
}).label('opsLog');

export const opsPagination = {
	tags: ['api', 'audit-log'],
	description: '操作日志-分页查看',
	auth: debugCheck('disable-open-api-auth')
		? false
		: {
				mode: 'required',
				access: {
					// 只允许使用 URL Token 访问
					entity: 'app',
				},
		  },

	validate: {
		payload: Joi.object({
			page: Joi.number().default(1).description('当前页'),
			perPage: Joi.number().default(10).description('每页条目数'),
			filter: joiToFilterArg(opsLog)
				.append({
					messageSeacrh: Joi.string().description('消息正则过滤'),
					createTimeGTE: Joi.date().description('创建时间大于等于'),
					createTimeLTE: Joi.date().description('创建时间小于等于'),
				})
				.description('过滤条件'),
		}),
	},

	// response: {
	// 	schema: Joi.object({
	// 		errors: Joi.array().items(Joi.object().unknown()),
	// 		data: Joi.object({
	// 			AuditPagination: Joi.object({
	// 				count: Joi.number().description('总条数'),
	// 				pageInfo: Joi.object({
	// 					currentPage: Joi.number().description('当前页数'),
	// 					perPage: Joi.number().description('每页条数'),
	// 					pageCount: Joi.number().description('总页数'),
	// 					itemCount: Joi.number().description('总条数'),
	// 					hasNextPage: Joi.boolean().description('是否有下一页'),
	// 					hasPreviousPage: Joi.boolean().description('是否有上一页'),
	// 				}),
	// 				items: Joi.array().items(opsLog),
	// 			}).label('AuditPagination'),
	// 		}),
	// 	}).label('Response'),
	// },

	handler: {
		injectGraphql: {
			payloadBuilder(request) {
				const {
					payload: { page, perPage, filter },
				} = request;

				return JSON.stringify({
					variables: {
						page,
						perPage,
						filter: {
							...filter,
							type: 'ops',
						},
					},
					// #region query
					query: `
						query($page: Int = 1, $perPage: Int = 10, $filter: FilterFindManyAuditInput, $sort: SortFindManyAuditInput = _ID_DESC) {
							AuditPagination(page: $page, perPage: $perPage, filter: $filter, sort: $sort) {
								count
								pageInfo {
									currentPage
									perPage
									pageCount
									itemCount
									hasNextPage
									hasPreviousPage
								}
								items {
									_id
									module
									level
									username
									uid
									ip
									affects
									message
									detail
									actionType
									actionMessage
									logSource
									createTime
								}
							}
						}`,
					// #endregion
				});
			},
		},
	},
};

export const loginPagination = {
	tags: ['api', 'audit-log'],
	description: '登录日志-分页查看',
	auth: debugCheck('disable-open-api-auth')
		? false
		: {
				mode: 'required',
				access: {
					// 只允许使用 URL Token 访问
					entity: 'app',
				},
		  },

	validate: {
		payload: Joi.object({
			page: Joi.number().default(1).description('当前页'),
			perPage: Joi.number().default(10).description('每页条目数'),
			filter: joiToFilterArg(loginLog)
				.append({
					createTimeGTE: Joi.date().description('创建时间大于等于'),
					createTimeLTE: Joi.date().description('创建时间小于等于'),
				})
				.description('过滤条件'),
		}),
	},

	response: {
		schema: Joi.object({
			errors: Joi.array().items(Joi.object().unknown()),
			data: Joi.object({
				AuditPagination: Joi.object({
					count: Joi.number().description('总条数'),
					pageInfo: Joi.object({
						currentPage: Joi.number().description('当前页数'),
						perPage: Joi.number().description('每页条数'),
						pageCount: Joi.number().description('总页数'),
						itemCount: Joi.number().description('总条数'),
						hasNextPage: Joi.boolean().description('是否有下一页'),
						hasPreviousPage: Joi.boolean().description('是否有上一页'),
					}),
					items: Joi.array().items(loginLog),
				}).label('AuditPagination'),
			}),
		}).label('Response'),
	},

	handler: {
		injectGraphql: {
			payloadBuilder(request) {
				const {
					payload: { page, perPage, filter },
				} = request;

				return JSON.stringify({
					variables: {
						page,
						perPage,
						filter: {
							...filter,
							type: 'auth',
						},
					},
					// #region query
					query: `
						query($page: Int = 1, $perPage: Int = 10, $filter: FilterFindManyAuditInput, $sort: SortFindManyAuditInput = _ID_DESC) {
							AuditPagination(page: $page, perPage: $perPage, filter: $filter, sort: $sort) {
								count
								pageInfo {
									currentPage
									perPage
									pageCount
									itemCount
									hasNextPage
									hasPreviousPage
								}
								items {
									_id
									module
									level
									username
									uid
									ip
									message
									detail
									actionType
									actionMessage
									createTime
								}
							}
						}`,
					// #endregion
				});
			},
		},
	},
};

const opsCsvHeader = [
	{ name: '日志源', fieldPath: ['logSource'], render: x => LOG_SOURCE_DICT[x] },
	{ name: '模块', fieldPath: ['module'], render: x => LOG_MODULE_DICT[x] },
	{ name: '操作IP', fieldPath: ['ip'] },
	{ name: '域名', fieldPath: ['affects'] },
	{ name: '操作时间', fieldPath: ['createTime'], render: datetime },
	{ name: '操作人', fieldPath: ['username'] },
	{ name: '动作', fieldPath: ['actionType'], render: x => LOG_ACTION_DICT[x] },
	{ name: '操作信息', fieldPath: ['detail'] },
];

export const opsDownload = {
	tags: ['api', 'user'],
	description: '下载操作日志',
	auth: debugCheck('disable-open-api-auth')
		? false
		: {
				mode: 'required',
				// access: {
				// 	// 只允许使用 URL Token 访问
				// 	entity: 'app',
				// },
		  },

	timeout: { socket: false },

	validate: {
		query: joiToFilterArg(opsLog)
			.append({
				createTimeGTE: Joi.date().description('创建时间大于等于'),
				createTimeLTE: Joi.date().description('创建时间小于等于'),
			})
			.unknown()
			.description('过滤条件'),
	},

	async handler(request, h) {
		const {
			query,
			server: {
				app: {
					model: { Audit },
				},
			},
			auth: {
				credentials: { uid, permissionMap },
			},
		} = request;

		const stream = csvFormat({
			writeBOM: true,
			headers: opsCsvHeader.map(x => x.name),
		}).transform(row => {
			const data = opsCsvHeader.reduce((obj, { name, fieldPath, render }) => {
				obj[name] = pathOr('', fieldPath, row);
				if (render) obj[name] = render(obj[name]);
				return obj;
			}, {});
			return data;
		});

		const filter = {
			...extractQueryFieldByJoi({ query, schema: opsLog }),
			type: 'ops',
		};
		if (query.createTimeGTE) {
			set(filter, ['createTime', '$gte'], query.createTimeGTE);
			delete filter.createTimeGTE;
		}
		if (query.createTimeLTE) {
			set(filter, ['createTime', '$lte'], query.createTimeLTE);
			delete filter.createTimeLTE;
		}
		if (!permissionMap['auth\x01auditUserOps\x010']) {
			set(filter, ['uid'], uid);
		}

		setTimeout(async () => {
			try {
				for await (const record of Audit.find(filter)
					.sort({ _id: -1 })
					.limit(100000)) {
					stream.write(record);
				}
			} catch (err) {
				stream.end();
				const error = errorCodes['[14]操作日志下载失败'].from(err);
				request.log.error({ error }, error.rawMessage);
				return;
			}
			stream.end();
		});

		return h
			.response(stream)
			.header('Content-Type', 'text/csv')
			.header('Content-Disposition', 'attachment; filename="ops.csv"');
	},
};

const loginCsvHeader = [
	{ name: '操作时间', fieldPath: ['createTime'], render: datetime },
	{ name: '操作人', fieldPath: ['username'] },
	{ name: 'IP', fieldPath: ['ip'] },
	{ name: '动作', fieldPath: ['actionMessage'] },
	{ name: '状态', fieldPath: ['detail'] },
	{ name: '操作信息', fieldPath: ['message'] },
];
export const loginDownload = {
	tags: ['api', 'user'],
	description: '下载登录日志',
	auth: debugCheck('disable-open-api-auth')
		? false
		: {
				mode: 'required',
				access: {
					// 只允许使用 URL Token 访问
					entity: 'app',
				},
		  },

	timeout: { socket: false },

	validate: {
		query: joiToFilterArg(loginLog)
			.append({
				createTimeGTE: Joi.date().description('创建时间大于等于'),
				createTimeLTE: Joi.date().description('创建时间小于等于'),
			})
			.unknown()
			.description('过滤条件'),
	},

	async handler(request, h) {
		const {
			query,
			server: {
				app: {
					model: { Audit },
				},
			},
			auth: {
				credentials: { uid, permissionMap },
			},
		} = request;

		const stream = csvFormat({
			writeBOM: true,
			headers: loginCsvHeader.map(x => x.name),
		}).transform(row => {
			const data = loginCsvHeader.reduce((obj, { name, fieldPath, render }) => {
				obj[name] = pathOr('', fieldPath, row);
				if (render) obj[name] = render(obj[name]);
				return obj;
			}, {});
			return data;
		});

		const filter = {
			...extractQueryFieldByJoi({ query, schema: loginLog }),
			type: 'auth',
		};
		if (query.createTimeGTE) {
			set(filter, ['createTime', '$gte'], query.createTimeGTE);
			delete filter.createTimeGTE;
		}
		if (query.createTimeLTE) {
			set(filter, ['createTime', '$lte'], query.createTimeLTE);
			delete filter.createTimeLTE;
		}
		if (!permissionMap['auth\x01auditUserOps\x010']) {
			set(filter, ['uid'], uid);
		}

		setTimeout(async () => {
			try {
				for await (const record of Audit.find(filter).limit(100000)) {
					stream.write(record);
				}
			} catch (err) {
				stream.end();
				const error = errorCodes['[14]登录日志下载失败'].from(err);
				request.log.error({ error }, error.rawMessage);
				return;
			}
			stream.end();
		});

		return h
			.response(stream)
			.header('Content-Type', 'text/csv')
			.header('Content-Disposition', 'attachment; filename="login.csv"');
	},
};

const allCsvHead = [
	{ name: '操作人', fieldPath: ['username'] },
	{ name: '操作行为', fieldPath: ['actionMessage'], render: x => x || '- - -' },
	{
		name: '资产地址',
		fieldPath: ['affects'],
		render: affects => (affects.length > 0 ? affects.join(' ') : '- - - '),
	},
	{
		name: '操作时间',
		fieldPath: ['createTime'],
		render: datetime,
	},
	{ name: 'IP', fieldPath: ['ip'] },
	{ name: '详情', fieldPath: ['detail'] },
];

export const logDownload = {
	tags: ['api', 'user'],
	description: '下载操作日志',
	auth: debugCheck('disable-open-api-auth')
		? false
		: {
				mode: 'required',
				// access: {
				// 	// 只允许使用 URL Token 访问
				// 	entity: 'app',
				// },
		  },

	timeout: { socket: false },

	validate: {
		query: joiToFilterArg(opsLog)
			.append({
				createTimeGTE: Joi.date().description('创建时间大于等于'),
				createTimeLTE: Joi.date().description('创建时间小于等于'),
			})
			.unknown()
			.description('过滤条件'),
	},

	async handler(request, h) {
		const {
			query,
			auth: {
				credentials: { uid, permissionMap },
			},
			server: {
				app: {
					model: { Audit },
				},
			},
		} = request;

		const stream = csvFormat({
			writeBOM: true,
			headers: allCsvHead.map(x => x.name),
		}).transform(row => {
			const data = allCsvHead.reduce((obj, { name, fieldPath, render }) => {
				obj[name] = pathOr('', fieldPath, row);
				if (render) obj[name] = render(obj[name]);
				return obj;
			}, {});
			return data;
		});

		const filter = {
			...extractQueryFieldByJoi({ query, schema: opsLog }),
		};
		if (query.createTimeGTE) {
			set(filter, ['createTime', '$gte'], query.createTimeGTE);
			delete filter.createTimeGTE;
		}
		if (query.createTimeLTE) {
			set(filter, ['createTime', '$lte'], query.createTimeLTE);
			delete filter.createTimeLTE;
		}
		if (!permissionMap['auth\x01auditUserOps\x010']) {
			set(filter, ['uid'], uid);
		}
		setTimeout(async () => {
			try {
				for await (const record of Audit.find(filter)
					.sort({ _id: -1 })
					.limit(100000)) {
					stream.write(record);
				}
			} catch (err) {
				stream.end();
				const error = errorCodes['[14]操作日志下载失败'].from(err);
				request.log.error({ error }, error.rawMessage);
				return;
			}
			stream.end();
		});

		return h
			.response(stream)
			.header('Content-Type', 'text/csv')
			.header('Content-Disposition', 'attachment; filename="ops.csv"');
	},
};
